"""
Generate simple STL files for testing without VTK dependency
"""
import os
import numpy as np
import struct

def write_binary_stl(filename, vertices, faces):
    """
    Write a binary STL file
    
    Parameters:
    -----------
    filename : str
        Output file path
    vertices : numpy.ndarray
        Array of vertices (Nx3)
    faces : numpy.ndarray
        Array of face indices (Nx3)
    """
    with open(filename, 'wb') as fp:
        # Write header
        fp.write(b'\x00' * 80)  # Header
        fp.write(struct.pack('<I', len(faces)))  # Number of triangles
        
        # Write triangles
        for face in faces:
            # Get vertices of the triangle
            v1 = vertices[face[0]]
            v2 = vertices[face[1]]
            v3 = vertices[face[2]]
            
            # Calculate normal (simplified)
            normal = np.cross(v2 - v1, v3 - v1)
            norm = np.linalg.norm(normal)
            if norm > 0:
                normal = normal / norm
            else:
                normal = np.array([0, 0, 1])
            
            # Write normal and vertices
            fp.write(struct.pack('<3f', *normal))  # Normal
            fp.write(struct.pack('<3f', *v1))      # Vertex 1
            fp.write(struct.pack('<3f', *v2))      # Vertex 2
            fp.write(struct.pack('<3f', *v3))      # Vertex 3
            fp.write(struct.pack('<H', 0))         # Attribute byte count
    
    print(f"STL file written: {filename}")

def generate_cube(filename, size=1.0, quality='good'):
    """
    Generate a cube mesh
    
    Parameters:
    -----------
    filename : str
        Output file path
    size : float
        Size of the cube
    quality : str
        Quality of the mesh ('good', 'medium', 'poor')
    """
    # Define vertices of a cube
    half_size = size / 2
    vertices = np.array([
        [-half_size, -half_size, -half_size],  # 0
        [half_size, -half_size, -half_size],   # 1
        [half_size, half_size, -half_size],    # 2
        [-half_size, half_size, -half_size],   # 3
        [-half_size, -half_size, half_size],   # 4
        [half_size, -half_size, half_size],    # 5
        [half_size, half_size, half_size],     # 6
        [-half_size, half_size, half_size]     # 7
    ])
    
    # Define faces (triangles)
    faces = np.array([
        # Bottom face
        [0, 1, 2], [0, 2, 3],
        # Top face
        [4, 6, 5], [4, 7, 6],
        # Front face
        [0, 4, 1], [1, 4, 5],
        # Back face
        [2, 6, 3], [3, 6, 7],
        # Left face
        [0, 3, 4], [3, 7, 4],
        # Right face
        [1, 5, 2], [2, 5, 6]
    ])
    
    # Add more vertices for poor quality mesh
    if quality != 'good':
        # Add interior points with some randomness
        n_extra = 20 if quality == 'medium' else 50
        extra_vertices = []
        
        for _ in range(n_extra):
            # Random point inside the cube
            point = np.random.uniform(-half_size, half_size, 3)
            
            # Add noise based on quality
            if quality == 'medium':
                noise = np.random.normal(0, 0.05, 3)
            else:  # poor
                noise = np.random.normal(0, 0.15, 3)
            
            point += noise
            # Ensure point is inside the cube
            point = np.clip(point, -half_size, half_size)
            extra_vertices.append(point)
        
        # Add extra vertices
        extra_vertices = np.array(extra_vertices)
        original_vertex_count = len(vertices)
        vertices = np.vstack([vertices, extra_vertices])
        
        # Add extra faces connecting to interior points
        extra_faces = []
        for i in range(original_vertex_count, len(vertices)):
            # Connect to random existing vertices to create poor quality triangles
            for _ in range(3):
                v1 = i
                v2 = np.random.randint(0, len(vertices))
                v3 = np.random.randint(0, len(vertices))
                if v1 != v2 and v2 != v3 and v1 != v3:
                    extra_faces.append([v1, v2, v3])
        
        # Add extra faces
        if extra_faces:
            extra_faces = np.array(extra_faces)
            faces = np.vstack([faces, extra_faces])
    
    # Write STL file
    write_binary_stl(filename, vertices, faces)

def generate_sphere(filename, radius=1.0, resolution=20, quality='good'):
    """
    Generate a sphere mesh
    
    Parameters:
    -----------
    filename : str
        Output file path
    radius : float
        Radius of the sphere
    resolution : int
        Resolution of the sphere
    quality : str
        Quality of the mesh ('good', 'medium', 'poor')
    """
    # Generate sphere vertices using spherical coordinates
    vertices = []
    for i in range(resolution + 1):
        phi = np.pi * i / resolution
        for j in range(resolution * 2):
            theta = 2 * np.pi * j / (resolution * 2)
            x = radius * np.sin(phi) * np.cos(theta)
            y = radius * np.sin(phi) * np.sin(theta)
            z = radius * np.cos(phi)
            vertices.append([x, y, z])
    
    vertices = np.array(vertices)
    
    # Generate faces
    faces = []
    for i in range(resolution):
        for j in range(resolution * 2):
            p1 = i * (resolution * 2) + j
            p2 = p1 + 1
            if j == resolution * 2 - 1:
                p2 = i * (resolution * 2)
            
            p3 = (i + 1) * (resolution * 2) + j
            p4 = p3 + 1
            if j == resolution * 2 - 1:
                p4 = (i + 1) * (resolution * 2)
            
            # Add two triangles
            faces.append([p1, p2, p3])
            faces.append([p2, p4, p3])
    
    faces = np.array(faces)
    
    # Deform the mesh based on quality
    if quality != 'good':
        # Apply random deformation
        noise_scale = 0.05 if quality == 'medium' else 0.15
        noise = np.random.normal(0, noise_scale * radius, vertices.shape)
        vertices += noise
    
    # Write STL file
    write_binary_stl(filename, vertices, faces)

def main():
    """Main function"""
    # Create output directory
    output_dir = os.path.dirname(os.path.abspath(__file__))
    
    # Generate cube meshes
    generate_cube(os.path.join(output_dir, 'cube_good.stl'), quality='good')
    generate_cube(os.path.join(output_dir, 'cube_medium.stl'), quality='medium')
    generate_cube(os.path.join(output_dir, 'cube_poor.stl'), quality='poor')
    
    # Generate sphere meshes
    generate_sphere(os.path.join(output_dir, 'sphere_good.stl'), quality='good')
    generate_sphere(os.path.join(output_dir, 'sphere_medium.stl'), quality='medium')
    generate_sphere(os.path.join(output_dir, 'sphere_poor.stl'), quality='poor')

if __name__ == "__main__":
    main() 